using System;
using System.Collections.Generic;
using System.Text;
using SanFS.MediaChangerLibrary;
using SanFS.DiscLibrary;
using System.Threading;
using SanFS.Logging;

namespace SanFS.MediaChangerService
{

    /// <summary>
    /// An service object which has its own thread to manage scheduling of mediachanger requests
    /// </summary>
    public class MediaChangerService
    {
        private IMediaChanger       mMediaChanger;
        private Thread              mSvcThread;
        private Queue<IJobRequest>  mJobQueue;
        private ManualResetEvent    mJobQueueRst;

        public MediaChangerService(IMediaChanger Changer)
        {
            mMediaChanger = Changer;
            mSvcThread = new Thread(new ThreadStart(SvcThread));
            mJobQueue = new Queue<IJobRequest>();
            mJobQueueRst = new ManualResetEvent(false);
        }
        
        #region Methods which are run from client thread context
        public void Start()
        {
            mSvcThread.Start();
        }
        
        public void Stop()
        {
            mSvcThread.Abort();
        }

        public IJobRequest ScanInventory(int Start, int End) // XXX: Make these IStorageElements
        {
            return QueueJob(new ScanInventoryJob(this, Start, End));
        }
        
        private IJobRequest QueueJob(IJobRequest Jr)
        {
            lock (mJobQueue)
            {
                mJobQueue.Enqueue(Jr);
                mJobQueueRst.Set();
            }
            return Jr;
        }
        #endregion
        
        #region Methods which are run from service thread context
        private void SvcThread()
        {
            bool    shutdown = false;
            
            try
            {
                Log.WriteLine(Log.Layer.Scheduler, Log.Type.Informative, "Starting up..");
                while(!shutdown)
                {
                    mJobQueueRst.WaitOne();                     // Sleep on the queue rst if theres nothing to do
                    IJobRequest jr = mJobQueue.Dequeue();       // Woken up, must be something in the queue
                    try
                    {
                        ((JobRequest)jr).ExecuteJob();
                        jr.WaitEvent.Set();                     // Unblock any threads waiting on the job
                        ((JobRequest)jr).EmitJobCompleted();
                    }
                    catch (Exception ex)
                    {
                        if (ex is ThreadAbortException)
                        {
                            Log.WriteLine(Log.Layer.Scheduler, Log.Type.Error, "Job Aborted");
                            ((JobRequest)jr).ResetPending();
                            mJobQueue.Enqueue(jr);

                        }
                        else
                        {
                            Log.WriteLine(Log.Layer.Scheduler, Log.Type.Error, "Job failed ({0})", ex.ToString());
                            jr.WaitEvent.Set();                 // Unblock any threads waiting on the job
                        }
                    }
                    finally
                    {
                        lock (mJobQueue)
                        {
                            if (mJobQueue.Count == 0)
                                mJobQueueRst.Reset();           // Queue is empty, we want to sleep on the next loop
                        }
                    }
                }
                Log.WriteLine(Log.Layer.Scheduler, Log.Type.Informative, "Shut down.");
            }
            catch (Exception ex)
            {
                Log.WriteLine(Log.Layer.Scheduler, Log.Type.Critical, ex.ToString());
                throw;
            }
        }

        #endregion
        
        #region Properties (can be used from either context)

        public IMediaChanger MediaChanger
        {
            get { return mMediaChanger; }
        }
             
        #endregion
        
    }  // END CLASS
}  // END NAMESPACE
